package cn.hicard.risk.task;

import java.io.File;
import java.io.FileOutputStream;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.TimerTask;
import org.apache.log4j.Logger;
import sun.net.TelnetInputStream;
import sun.net.ftp.FtpClient;
import cn.hicard.risk.common.Constant;
import cn.hicard.risk.dao.BaseDao;
import cn.hicard.risk.entity.BdTradefileinfo;
import cn.hicard.risk.action.bd.CallorderGenerateFlex;
import cn.hicard.risk.action.bd.PeriodFreezeFlex;
import cn.hicard.risk.io.PosTradeFileAnalyzer;
import cn.hicard.risk.service.bd.CallorderService;
import cn.hicard.risk.service.bd.MerchInfoService;
import cn.hicard.risk.service.bd.PeriodFreezeService;
import cn.hicard.risk.service.bd.TradefileinfoService;
import cn.hicard.util.AppConfig;
import cn.hicard.util.DateHelp;
import cn.hicard.util.SpringContext;

/**
 * 定时器任务类，定时检测文件，并分析文件
 * @author zhangy
 *
 */
public class FileMonitorTask extends TimerTask {
	
	/** 日志记录工具 */
	private static Logger logger = Logger.getLogger(Constant.LOGGER_MAIN);
	
	/** 传统POS交易数据业务类 */
	private TradefileinfoService tradefileinfoService;
	/** 调单数据业务类 */ 
	private CallorderService callorderservice;
	/** 清算周期冻结解冻业务类*/
	private PeriodFreezeService periodFreezeService;
	/** 商户数据业务类 */ 
	private MerchInfoService merchInfoService;
	/** 调单数据生成类 */
	private CallorderGenerateFlex callorderGenerateFlex; 
	/** 清算周期冻结解冻类*/
	private PeriodFreezeFlex periodFreezeFlex;
	
	/**
	 * 初始化操作，由于定时器是由系统自动执行，而非户请求操作，
	 * 因此需要使用的业务类不会被Spring初始化需要手动执行初始化。
	 */
	private void initialize(){
		if(tradefileinfoService == null){
			tradefileinfoService = (TradefileinfoService) SpringContext.getBean("tradefileinfoService");
		}
		if(callorderservice == null){
			callorderservice = (CallorderService)SpringContext.getBean("callorderService");
		}
		if(merchInfoService == null){
			merchInfoService =  (MerchInfoService)SpringContext.getBean("merchInfoService");
		}

		if(periodFreezeService == null){
			periodFreezeService = (PeriodFreezeService)SpringContext.getBean("periodFreezeService");
		}
		if(callorderGenerateFlex == null){
			callorderGenerateFlex = new CallorderGenerateFlex();
			callorderGenerateFlex.setCallorderservice(callorderservice);
			callorderGenerateFlex.setMerchInfoService(merchInfoService);
			callorderGenerateFlex.setTradefileinfoService(tradefileinfoService);
		}
		if(periodFreezeFlex == null){
			periodFreezeFlex = new PeriodFreezeFlex();
			periodFreezeFlex.setPeriodFreezeService(periodFreezeService);
		}
	}
	
	@Override
	public void run() {
		// 调用初始化
		initialize();
		
		//解冻上个工作日冻结的T+2商户
		try {
			periodFreezeFlex.doUnFreeeze();			
		} catch (Exception e) {
			logger.error("解冻上一工作日周期冻结信息时出错", e);
			
		}
		
		// 从数据库检索最后一次导入的数据的交易日期
		// 前一天
		String yesterday = DateHelp.format(new Date(System.currentTimeMillis() - 24*60*60*1000), "yyyyMMdd");
		// 最后交易日期
		String lastdate = "";
		@SuppressWarnings("unchecked")
		BaseDao<BdTradefileinfo> baseDao = (BaseDao<BdTradefileinfo>)SpringContext.getBean("baseDao");		
		// String hql = "SELECT transdate FROM BdTradefileinfo ORDER BY transdate DESC LIMIT 1";
		// HQL不支持LIMIT
		String hql = "SELECT MAX(transdate) FROM BdTradefileinfo";
		try {
			@SuppressWarnings("unchecked")
			List list = baseDao.findFieldsByHql(hql);
			if(list == null || list.size() == 0 || list.get(0) == null){
				logger.warn("数据库没有交易数据或交易日期为空，无法确认最后交易日期，默认使用系统当前日期的前一天:" + yesterday);				
				lastdate = yesterday;
			}else{
				lastdate = list.get(0).toString().substring(0, 8); // 只取yyyyMMdd，以防变动增加的时分秒
			}
		} catch (Exception e) {
			logger.error("从FTP下载文件时连接数据库出错", e);
			return;
		}
		
		// 计算[最后一笔交易日期]到[系统当前时间的前一天]的所有日期集合
		List<String> days = new ArrayList<String>();
		Calendar cln = Calendar.getInstance();	
		// 从当前日期的前一天算起，一直减到最后交易日期lastdate
		cln.add(Calendar.DATE, -1); 
		String date = DateHelp.format(cln.getTime(), "yyyyMMdd");		
		while(Integer.valueOf(date) >= Integer.valueOf(lastdate)){
			days.add(date);
			cln.add(Calendar.DATE, -1);
			date = DateHelp.format(cln.getTime(), "yyyyMMdd");
		}

		// 日志记录要下载的文件名列表：
		String fileNames = "";
		for (String temp : days) {
			fileNames += ("\t" + temp + "\r\n");
		}
		logger.info("从最后一笔交易的日期" + lastdate + "到昨天" + yesterday + "一共有如下日期的[" + days.size() + "]个文件需要下载：\r\n" + fileNames);
						
		// 处理指定日期的文件
		for (String day : days) {

			// 从FTP下载文件
			downloadFile(day);

			// 读文件
			readFile(day);
			
			// 生成紧急调单数据、T+2清算周期冻结数据
			int count = callorderGenerateFlex.generate(day);
			if(count == -1){
				logger.error("定时执行任务 生成紧急调单数据 时发生错误");
			}else{
				logger.info("生成紧急调单数据完成，成功生成的数量有" + count);
			}
		}		
	}
	
	/**
	 * 下载指定日期的文件
	 * @param day
	 */
	private void downloadFile(String day){
		logger.debug("downloadFile开始执行:" + day);
		// FTP下载所需参数
		String host = AppConfig.get("monitor_ftphost");
		int port = Integer.valueOf(AppConfig.get("monitor_ftpport"));
		String username = AppConfig.get("monitor_ftpuser");
		String password = AppConfig.get("monitor_ftppassword");
		String remotepath = AppConfig.get("monitor_ftppath");
		String localpath = AppConfig.get("monitor_localpath");
		String posfilename = AppConfig.get("monitor_posfilename");
		// 匹配文件名
//		posfilename = DateHelp.match(posfilename, day);		
		FileOutputStream fos = null;		
		FtpClient client = new FtpClient();
		
		try {		
			
			logger.debug("开始检查本地文件是否已经下载");
			// 检查本地文件是否已经下载
			if (new File(localpath + posfilename).exists()
			 || new File(localpath + posfilename + "_").exists()){
				logger.info("文件[" + posfilename + "]已经被下载到本地，跳过本次下载");
				return;
			}
			
			logger.debug("开始连接到服务器"+host+":"+port);
			client.openServer(host, port);
			logger.debug("开始登录用户"+username);
			client.login(username, password);
			logger.info("连接服务器并登录用户成功");
			// 以二进制数据方式传送文件
			client.binary();
			logger.debug("服务器文件路径：" + remotepath + posfilename);
			logger.debug("开始下载文件到本地：" + localpath + posfilename);
			// 下载文件并保存到本地  / [.buf]表示该文件是临时文件
			File file = new File(localpath + posfilename + ".buf");
			fos = new FileOutputStream(file);
			
			client.cd(remotepath);
			TelnetInputStream inputStream = client.get(posfilename);
			logger.info("开始接收数据");
			
			byte[] buff = new byte[1024];
			int len = -1;
			while((len = inputStream.read(buff)) != -1){
				fos.write(buff, 0 ,len);
			}
			
			fos.close();
			logger.info("接收数据完成，关闭本地文件流，文件大小：" + file.length());			
			// 如果文件为空，则将文件删除
			if(file.length() == 0){
				file.delete();
			}
			// 有数据则将文件重命为正常
			else{
				file.renameTo(new File(localpath + posfilename));
			}
			logger.info("文件[" + posfilename + "]下载完成");
		} catch (Exception e) {
			logger.error("FTP下载文件时出错", e);
		} finally {
			logger.debug("downloadFile完成执行");
			
			// 始终都要关闭FTP与文件输出流
			try {
				client.closeServer();
			} catch (Exception e2) {				
			}
			try {
				if (fos != null)
					fos.close();
			} catch (Exception e2) {
			}			
		}		
	}
	
	/**
	 * 检查并读取未操作的文件
	 */
	private void readFile(String day){
		// 配置路径及文件名
		String localpath = AppConfig.get("monitor_localpath");
		String posfilename = AppConfig.get("monitor_posfilename");
		// 匹配文件名
//		posfilename = DateHelp.match(posfilename, day);
		// 检查是否已经处理过该文件
		if(new File(localpath + posfilename + "_").exists()){
			logger.info("文件[" + localpath + posfilename + "]已经被处理，跳过次处理");
			return;
		}
				
		// 检查本地文件是否存在
		File file = new File(localpath + posfilename);
		if( ! file.exists()){
			logger.info("文件[" + localpath + posfilename + "]不存在");
			return;
		}
		
		// 实例化传统POS交易文件分析器
		PosTradeFileAnalyzer analyzer = new PosTradeFileAnalyzer();
		
		// 文件处理的结果
		logger.info("开始分析交易文件：" + file.getName());
		List<BdTradefileinfo> list = null;
		try {
			list = analyzer.analyze(file);
		} catch (Exception e) {
			logger.error("分析交易文件时出错：" + file.getName(), e);
			file.renameTo(new File(file.getPath() + "_"));
			return;
		}
		logger.info("分析完毕准备保存，交易数据量：" + list.size());
		if(list==null || list.size() == 0){
			logger.info("文件[" + file.getName() + "]内没有数据");
			file.renameTo(new File(file.getPath() + "_"));
			return;
		}
		try {
			String existsSysno = tradefileinfoService.saveList(list);
			if(existsSysno==null || existsSysno.length()==0){
				logger.info("文件[" + file.getName() + "]内的" + list.size() + "条数据已成功保存");
			}else{
				logger.warn("文件[" + file.getName() + "]已放弃保存如下原本已存在的交易参考号的数据:" + existsSysno);
			}
		} catch (Exception e) {
			logger.error("保存文件[" + file.getName() + "]的数据到数据库时出错", e);
		}
		file.renameTo(new File(file.getPath() + "_"));
	}
}
